同一種資料在不同平台有各自呈現方式,除了抓取資料外,也要整理成方便使用的資料格式,保有乾淨的資料源頭,後續不管是在搜尋、排序上都會更快更方便,以下以整理薪資資料為例。
觀察三大求職平台,會出現的薪資格式大致如下
104:
月薪34,000~52,000元
月薪50,000元以上
年薪800,000~1,000,000元
yourator:
NT$ 50,000 - 65,000 (月薪)
NT$ 900,000 - 1,600,000 (年薪)
面議(經常性薪資達4萬元)
NT$ 700,000 - (年薪)
CakeResume:
4萬 ~ 10萬 TWD / 月
56萬 ~ 90萬 TWD / 年
4.8萬+ TWD / 月
初步觀察
薪資的給法主要有年薪或是月薪,沒有固定公式可以換算,將年薪/12 或是月薪x12 不一定正確,沒辦法將兩者混在一起排序。
其中僅 104 有「待遇高低」的排序方式,作法是將年薪、月薪職缺先各自排序,先顯示年薪排序再顯示月薪排序結果。若薪資非固定數值,使用最小值做排序依據。
*截圖取自 104.com.tw。年薪 420,000 會排在月薪 130,000 之前
若要使用薪資做排序,可將薪資整理為一個陣列 [最小值, 最大值]
,只留下數值,最後 render 在畫面時,再決定 NT$
、元
、千分號
等字串格式顯示。
-
或 ~
切分,將 salary 轉成陣列,例如 56萬 ~ 90萬 TWD / 年
會變成 ['56萬', '90萬 TWD / 年']
56萬
會變成 560000
或是 4.8萬
會變成 48000
NT$ 900,000
會變成 900000
原始數據
[
{
"id": 1,
"name": "遊戲前端工程師助理",
"salary": "月薪34,000~52,000元"
},
{
"id": 2,
"name": "前端網頁工程師(接案)",
"salary": "年薪800,000~1,000,000元"
},
{
"id": 3,
"name": "Vue.js前端工程師",
"salary": "NT$ 50,000 - 65,000 (月薪)"
},
{
"id": 4,
"name": "網頁前端工程師",
"salary": "NT$ 900,000 - 1,600,000 (年薪)"
},
{
"id": 5,
"name": "遊戲前端工程師助理",
"salary": "56萬 ~ 90萬 TWD / 年"
},
{
"id": 6,
"name": "網站前端工程師(台南)",
"salary": "4萬 ~ 10萬 TWD / 月"
},
{
"id": 7,
"name": "網站前端工程師(台南)",
"salary": "面議(經常性薪資達4萬元)"
},
{
"id": 8,
"name": "網站前端工程師(台南)",
"salary": "4.8萬+ TWD / 月"
}
]
整理薪資
const jobList = require("./salary.json");
const keyword = {
year: "年",
month: "月",
tenThousand: "萬",
dollar: "元",
};
let data = jobList.map((job) => {
let salary = [],
salaryType = "";
// salaryType 區分類別
if (job.salary.includes(keyword.month)) {
salaryType = "month";
} else if (job.salary.includes(keyword.year)) {
salaryType = "year";
} else {
salaryType = "other";
}
// 以 - 或 ~ 切分,將 salary 轉成陣列
salary = job.salary.split(/-|~/);
salary = salary.map((item) => {
// match 萬字前面的數值
const withWordRegex = new RegExp(`\\d+\.?\\d?(?=${keyword.tenThousand})`);
// match 數值
const withThousandSeparatorRegex = /(\d+,?)+/;
let value;
if (item.includes(keyword.tenThousand)) {
value = item.match(withWordRegex)[0] * 10000;
} else if (withThousandSeparatorRegex.test(item)) {
value = Number(
item.match(withThousandSeparatorRegex)[0].replaceAll(",", "")
);
} else {
value = item;
}
return value;
});
return {
...job,
salary,
salaryType,
};
});
處理後的資料
[
{
id: 1,
name: '遊戲前端工程師助理',
salary: [ 34000, 52000 ],
salaryType: 'month'
},
{
id: 2,
name: '前端網頁工程師(接案)',
salary: [ 800000, 1000000 ],
salaryType: 'year'
},
{
id: 3,
name: 'Vue.js前端工程師',
salary: [ 50000, 65000 ],
salaryType: 'month'
},
{
id: 4,
name: '網頁前端工程師',
salary: [ 900000, 1600000 ],
salaryType: 'year'
},
{
id: 5,
name: '遊戲前端工程師助理',
salary: [ 560000, 900000 ],
salaryType: 'year'
},
{
id: 6,
name: '網站前端工程師(台南)',
salary: [ 40000, 100000 ],
salaryType: 'month'
},
{
id: 7,
name: '網站前端工程師(台南)',
salary: [ 40000 ],
salaryType: 'other'
},
{
id: 8,
name: '網站前端工程師(台南)',
salary: [ 48000 ],
salaryType: 'month'
}
]